/* global travelgoFlightSearch */ (function () { var cfg = typeof travelgoFlightSearch === "object" && travelgoFlightSearch ? travelgoFlightSearch : {}; var API_KEY = cfg.apiKey || ""; var BASE_URL = cfg.baseUrl || "https://www.searchapi.io/api/v1/search"; var REST_URL = String(cfg.restUrl || "").replace(/\/$/, ""); var AIRPORTS_URL = String(cfg.airportsUrl || "").replace(/\/$/, ""); var MAPS_NEARBY_URL = String(cfg.mapsNearbyUrl || "").replace(/\/$/, ""); var HAS_GOOGLE_MAPS = !!cfg.hasGoogleMaps; var useRestProxy = REST_URL.length > 0; var S = cfg.strings || {}; var PRICING = cfg.pricing || {}; var RESULTS_PAGE_URL = String(cfg.resultsPageUrl || "").trim(); var AJAX_URL = String(cfg.ajaxUrl || ""); var BOOKING_NONCE = String(cfg.bookingNonce || ""); var BOOKING_ENABLED = !!cfg.bookingEnabled; var AIRLINE_LOGOS_URI = String(cfg.airlineLogosUri || "").replace(/\/$/, ""); var BUNDLED_AIRLINE_ICAO = Array.isArray(cfg.bundledAirlineIcao) ? cfg.bundledAirlineIcao : []; var REMOTE_AIRLINE_LOGO_URL = String(cfg.remoteAirlineLogoUrl || "https://images.kiwi.com/airlines/64/%s.png"); var ASSET_VERSION = String(cfg.assetVersion || ""); if (!useRestProxy && !API_KEY) return; var homeRoot = document.getElementById("travelgo-skyscout-root"); var resultsRoot = document.getElementById("travelgo-flight-results-root"); var isHome = !!(homeRoot && homeRoot.getAttribute("data-has-key") === "1"); var isResults = !!( resultsRoot && resultsRoot.getAttribute("data-has-key") === "1" ); if (!isHome && !isResults) return; var ROOT = resultsRoot || homeRoot; function $(id) { return ROOT ? ROOT.querySelector("#" + id) : null; } /** Results list / price insight / sort (flight results page only) */ function res$(suffix) { return resultsRoot ? resultsRoot.querySelector("#travelgo-ss-" + suffix) : null; } var state = { tripType: "one_way", from: "DEL", to: "BOM", departDate: "", returnDate: "", adults: "1", children: "0", infants: "0", travelClass: "economy", stops: "any", currency: "INR", sortBy: "top_flights", fareType: "regular", currentResults: [], allFlights: [], currentMeta: null, lastRenderedFlights: null, }; var filterState = { stopsNonstop: false, stopsOne: false, stopsTwoPlus: false, depBefore6: false, dep6to12: false, dep12to18: false, depAfter18: false, priceMaxUser: null, airlinesAllowed: null, }; var filtersWired = false; var fareCalLoading = false; var fareCalCache = {}; var popoverDraft = { adults: "1", children: "0", infants: "0", travelClass: "economy", stops: "any", }; var airportNames = { DEL: "Indira Gandhi International Airport", BOM: "Chhatrapati Shivaji International Airport", MAA: "Chennai International Airport", BLR: "Kempegowda International Airport", HYD: "Rajiv Gandhi International Airport", CCU: "Netaji Subhas Chandra Bose International Airport", COK: "Cochin International Airport", TRV: "Trivandrum International Airport", JFK: "John F. Kennedy International Airport", LHR: "Heathrow Airport", DXB: "Dubai International Airport", SIN: "Singapore Changi Airport", BKK: "Suvarnabhumi Airport", KUL: "Kuala Lumpur International Airport", CDG: "Charles de Gaulle Airport", AMS: "Schiphol Airport", MAD: "Adolfo Suárez Madrid–Barajas Airport", FCO: "Leonardo da Vinci–Fiumicino Airport", NRT: "Narita International Airport", SYD: "Sydney Kingsford Smith Airport", LAX: "Los Angeles International Airport", ORD: "O'Hare International Airport", PNQ: "Pune Airport", GOI: "Goa International Airport", IXC: "Chandigarh International Airport", IXR: "Birsa Munda Airport", GAU: "Lokpriya Gopinath Bordoloi International Airport", JAI: "Jaipur International Airport", AMD: "Sardar Vallabhbhai Patel International Airport", ATQ: "Sri Guru Ram Dass Jee International Airport", IXB: "Bagdogra Airport", IXJ: "Jammu Airport", IXU: "Aurangabad Airport", NAG: "Dr. Babasaheb Ambedkar International Airport", LKO: "Chaudhary Charan Singh International Airport", IXM: "Madurai Airport", IXS: "Silchar Airport", IXG: "Belagavi Airport", BBI: "Biju Patnaik International Airport", PAT: "Jay Prakash Narayan Airport", IXA: "Agartala Airport", DIB: "Dibrugarh Airport", CCJ: "Calicut International Airport", CNN: "Kannur International Airport", TRZ: "Tiruchirappalli International Airport", VNS: "Lal Bahadur Shastri Airport", IXD: "Prayagraj Airport", STV: "Surat Airport", RPR: "Swami Vivekananda Airport", BDQ: "Vadodara Airport", IXE: "Mangalore International Airport", RUH: "King Khalid International Airport", JED: "King Abdulaziz International Airport", DOH: "Hamad International Airport", AUH: "Zayed International Airport", SHJ: "Sharjah International Airport", KWI: "Kuwait International Airport", MCT: "Muscat International Airport", CAI: "Cairo International Airport", ADD: "Bole International Airport", JNB: "O. R. Tambo International Airport", CPT: "Cape Town International Airport", }; var airportCity = { DEL: "Delhi", BOM: "Mumbai", MAA: "Chennai", BLR: "Bengaluru", HYD: "Hyderabad", CCU: "Kolkata", COK: "Kochi", TRV: "Thiruvananthapuram", JFK: "New York", LHR: "London", DXB: "Dubai", SIN: "Singapore", BKK: "Bangkok", KUL: "Kuala Lumpur", CDG: "Paris", AMS: "Amsterdam", MAD: "Madrid", FCO: "Rome", NRT: "Tokyo", SYD: "Sydney", LAX: "Los Angeles", ORD: "Chicago", PNQ: "Pune", GOI: "Goa", IXC: "Chandigarh", IXR: "Ranchi", GAU: "Guwahati", JAI: "Jaipur", AMD: "Ahmedabad", ATQ: "Amritsar", IXB: "Bagdogra", IXJ: "Jammu", IXU: "Aurangabad", NAG: "Nagpur", LKO: "Lucknow", IXM: "Madurai", IXS: "Silchar", IXG: "Belagavi", BBI: "Bhubaneswar", PAT: "Patna", IXA: "Agartala", DIB: "Dibrugarh", CCJ: "Kozhikode", CNN: "Kannur", TRZ: "Tiruchirappalli", VNS: "Varanasi", IXD: "Prayagraj", STV: "Surat", RPR: "Raipur", BDQ: "Vadodara", IXE: "Mangalore", RUH: "Riyadh", JED: "Jeddah", DOH: "Doha", AUH: "Abu Dhabi", SHJ: "Sharjah", KWI: "Kuwait City", MCT: "Muscat", CAI: "Cairo", ADD: "Addis Ababa", JNB: "Johannesburg", CPT: "Cape Town", }; var AIRPORT_LIST = []; Object.keys(airportCity).forEach(function (k) { if (k.length === 3 && /^[A-Z]{3}$/.test(k)) { AIRPORT_LIST.push({ iata: k, city: airportCity[k], name: airportNames[k] || "", }); } }); function upsertAirportList(items) { if (!Array.isArray(items) || !items.length) return; var seen = {}; AIRPORT_LIST.forEach(function (a) { var code = String(a.iata || "").toUpperCase(); if (code) seen[code] = true; }); items.forEach(function (a) { var code = String(a && a.iata ? a.iata : "").toUpperCase().replace(/[^A-Z]/g, "").substring(0, 3); if (!code || seen[code]) return; AIRPORT_LIST.push({ iata: code, city: String((a && a.city) || code), name: String((a && a.name) || "") }); if (!airportCity[code]) airportCity[code] = String((a && a.city) || code); if (!airportNames[code]) airportNames[code] = String((a && a.name) || ""); seen[code] = true; }); } async function fetchAirportSuggestionsRemote(raw) { if (!AIRPORTS_URL) return []; var q = String(raw || "").trim(); if (q.length < 2) return []; try { var url = AIRPORTS_URL + "?search=" + encodeURIComponent(q); var res = await fetch(url, { credentials: "same-origin" }); if (!res.ok) return []; var data = await res.json(); var arr = data && Array.isArray(data.airports) ? data.airports : []; upsertAirportList(arr); return arr; } catch (e) { return []; } } function resolveAirportQuery(raw) { var q = String(raw || "") .trim() .toLowerCase() .replace(/\s+/g, " "); if (!q) return null; var aliases = { trivandrum: "TRV", bangalore: "BLR", bengaluru: "BLR", bombay: "BOM", mumbai: "BOM", calcutta: "CCU", kolkata: "CCU", cochin: "COK", kochi: "COK", madras: "MAA", chennai: "MAA", calicut: "CCJ", kozhikode: "CCJ", }; if (aliases[q]) return aliases[q]; var alnum = q.replace(/[^a-z0-9]/g, ""); if (/^[a-z]{3}$/.test(alnum)) { var c3 = alnum.toUpperCase(); if (airportCity[c3]) return c3; } var best = null; var score = 0; for (var i = 0; i < AIRPORT_LIST.length; i++) { var a = AIRPORT_LIST[i]; var city = a.city.toLowerCase(); var name = (a.name || "").toLowerCase(); var code = a.iata.toLowerCase(); var s = 0; if (code === q || code === alnum) s = 100; else if (city === q) s = 95; else if (name === q) s = 93; else if (city.indexOf(q) === 0) s = 82; else if (city.indexOf(q) !== -1) s = 65; else if (name.indexOf(q) !== -1) s = 58; else if (q.length >= 2 && alnum.length >= 2 && code.indexOf(alnum) === 0) s = 45; if (s > score) { score = s; best = a.iata; } } return score >= 45 ? best : null; } function airportMatchesQuery(a, q) { if (!q) return true; var qq = q.toLowerCase(); var code = (a.iata || "").toLowerCase(); var city = (a.city || "").toLowerCase(); var name = (a.name || "").toLowerCase(); return ( code.indexOf(qq) === 0 || city.indexOf(qq) !== -1 || name.indexOf(qq) !== -1 ); } function airportSuggestions(raw, limit) { var q = String(raw || "").trim(); var out = AIRPORT_LIST.filter(function (a) { return airportMatchesQuery(a, q); }); out.sort(function (a, b) { var aq = q.toLowerCase(); var ac = (a.iata || "").toLowerCase(); var bc = (b.iata || "").toLowerCase(); if (aq) { var aStarts = ac.indexOf(aq) === 0 ? 0 : 1; var bStarts = bc.indexOf(aq) === 0 ? 0 : 1; if (aStarts !== bStarts) return aStarts - bStarts; } return (a.city || "").localeCompare(b.city || ""); }); return out.slice(0, limit || 10); } var classLabels = { economy: "Economy", premium_economy: "Premium economy", business: "Business", first_class: "First class", }; function metaLine(code) { var c = code.toUpperCase().substring(0, 3); var n = airportNames[c]; return n ? c + ", " + n : c + " Airport"; } function cityLine(code) { var c = code.toUpperCase().substring(0, 3); return airportCity[c] || c; } function applyPricingToFare(rawFare) { var base = Number(rawFare || 0); if (!isFinite(base) || base < 0) base = 0; var markupPercent = Number(PRICING.markupPercent || 0); var convenienceFee = Number(PRICING.convenienceFee || 0); var couponType = String(PRICING.couponType || "none"); var couponValue = Number(PRICING.couponValue || 0); var couponCode = String(PRICING.couponCode || ""); var cancelType = String(PRICING.cancelType || "none"); var cancelValue = Number(PRICING.cancelValue || 0); var refundNote = String(PRICING.refundNote || ""); var markupAmount = base * (markupPercent / 100); var gross = Math.max(0, base + markupAmount + Math.max(0, convenienceFee)); var couponAmount = 0; if (couponType === "percent") couponAmount = gross * (couponValue / 100); if (couponType === "fixed") couponAmount = couponValue; couponAmount = Math.min(Math.max(0, couponAmount), gross); var cancelAmount = 0; if (cancelType === "percent") cancelAmount = gross * (cancelValue / 100); if (cancelType === "fixed") cancelAmount = cancelValue; cancelAmount = Math.min(Math.max(0, cancelAmount), gross); return { baseFare: Math.round(base * 100) / 100, markupPercent: markupPercent, markupAmount: Math.round(markupAmount * 100) / 100, convenienceFee: Math.round(Math.max(0, convenienceFee) * 100) / 100, grossTotal: Math.round(gross * 100) / 100, couponType: couponType, couponValue: couponValue, couponCode: couponCode, couponAmount: Math.round(couponAmount * 100) / 100, cancelType: cancelType, cancelValue: cancelValue, cancelAmount: Math.round(cancelAmount * 100) / 100, estimatedRefund: Math.round(Math.max(0, gross - cancelAmount) * 100) / 100, refundNote: refundNote, }; } function formatClockTime(raw) { var s = String(raw || "").trim(); if (!s) return "--:--"; var m = s.match(/(\d{1,2}):(\d{2})/); if (!m) return s; var hh = ("0" + m[1]).slice(-2); var mm = m[2]; return hh + ":" + mm; } function pad2(n) { return ("0" + Number(n)).slice(-2); } /** * Canonical YYYY-MM-DD for search + segment labels. * Accepts HTML date input, DD-MM-YYYY / DD/MM/YYYY (day-first when ambiguous), and ISO. */ function normalizeSearchDateToIso(raw) { var s = String(raw || "").trim(); if (!s) return ""; if (/^\d{4}-\d{2}-\d{2}$/.test(s)) return s; var m = s.match(/^(\d{1,2})[\/.\-](\d{1,2})[\/.\-](\d{4})$/); if (m) { var p1 = parseInt(m[1], 10); var p2 = parseInt(m[2], 10); var y = parseInt(m[3], 10); var day; var month; if (p1 > 12) { day = p1; month = p2; } else if (p2 > 12) { month = p1; day = p2; } else { day = p1; month = p2; } if (y < 1900 || month < 1 || month > 12 || day < 1 || day > 31) return ""; return y + "-" + pad2(month) + "-" + pad2(day); } var d = new Date(s); if (!isNaN(d.getTime())) { return d.getFullYear() + "-" + pad2(d.getMonth() + 1) + "-" + pad2(d.getDate()); } return ""; } /** Split API time into Y-m-d (if present) and HH:MM clock. */ function parseAirportDateTime(raw) { var s = String(raw || "").trim(); if (!s) return { iso: "", clock: "" }; var m = s.match(/^(\d{4}-\d{2}-\d{2})[ T](\d{1,2}:\d{2})/); if (m) return { iso: m[1], clock: formatClockTime(m[2]) }; m = s.match(/^(\d{4})[\/](\d{2})[\/](\d{2})[ T](\d{1,2}:\d{2})/); if (m) return { iso: m[1] + "-" + m[2] + "-" + m[3], clock: formatClockTime(m[4]) }; return { iso: "", clock: formatClockTime(s) }; } function clockToMins(raw) { var m = String(raw || "").match(/(\d{1,2}):(\d{2})/); if (!m) return null; return parseInt(m[1], 10) * 60 + parseInt(m[2], 10); } /** Short date (e.g. "14 Apr") + time for segment rows. */ function formatDateWithTime(isoDate, rawTimeForClock) { var t = formatClockTime(rawTimeForClock); var iso = normalizeSearchDateToIso(isoDate); if (!iso) return t || "--:--"; var d = new Date(iso + "T12:00:00"); if (isNaN(d.getTime())) return t; var datePart = d.toLocaleDateString("en-IN", { day: "numeric", month: "short" }); return datePart + " " + t; } function normalizeCurrency(raw) { var c = String(raw || "").toUpperCase(); return c === "USD" ? "USD" : "INR"; } function currencyLocale(c) { return c === "USD" ? "en-US" : "en-IN"; } function currencySymbol(c) { return c === "USD" ? "$" : "₹"; } function formatMoney(amount) { var n = Number(amount || 0); if (!isFinite(n)) n = 0; return currencySymbol(state.currency) + n.toLocaleString(currencyLocale(state.currency)); } function addDaysIso(iso, days) { var norm = normalizeSearchDateToIso(iso); if (!norm) return String(iso || ""); var d = new Date(norm + "T12:00:00"); if (isNaN(d.getTime())) return norm; d.setDate(d.getDate() + days); return d.toISOString().split("T")[0]; } function diffDaysIso(a, b) { var na = normalizeSearchDateToIso(a); var nb = normalizeSearchDateToIso(b); if (!na || !nb) return 0; var da = new Date(na + "T12:00:00"); var db = new Date(nb + "T12:00:00"); if (isNaN(da.getTime()) || isNaN(db.getTime())) return 0; return Math.round((db.getTime() - da.getTime()) / 86400000); } function fmtFareCalDay(iso) { var d = new Date(iso + "T12:00:00"); if (isNaN(d.getTime())) return iso; var w = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; var m = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; return w[d.getDay()] + ", " + m[d.getMonth()] + " " + d.getDate(); } function parseLowestPriceFromData(data) { if (!data || typeof data !== "object") return null; var pi = data.price_insights && Number(data.price_insights.lowest_price || 0); if (isFinite(pi) && pi > 0) return pi; var prices = []; var best = Array.isArray(data.best_flights) ? data.best_flights : []; var other = Array.isArray(data.other_flights) ? data.other_flights : []; best.concat(other).forEach(function (f) { var p = Number(f && f.price ? f.price : 0); if (isFinite(p) && p > 0) prices.push(p); }); if (!prices.length) return null; prices.sort(function (a, b) { return a - b; }); return prices[0]; } async function fetchFareForDate(outDate, retDate) { var apiTrip = state.tripType === "one_way" ? "one_way" : "round_trip"; var cacheKey = state.from + "|" + state.to + "|" + outDate + "|" + (retDate || "") + "|" + apiTrip + "|" + state.travelClass + "|" + state.adults + "|" + state.children + "|any"; if (fareCalCache.hasOwnProperty(cacheKey)) return fareCalCache[cacheKey]; var payload = { departure_id: state.from, arrival_id: state.to, outbound_date: outDate, flight_type: apiTrip, travel_class: state.travelClass, adults: parseInt(state.adults, 10) || 1, children: parseInt(state.children, 10) || 0, // Use broadest stop filter for true cheapest-day signal. stops: "any", sort_by: "price", currency: state.currency, gl: "in", hl: "en", }; if (apiTrip === "round_trip" && retDate) payload.return_date = retDate; try { var data; if (useRestProxy) { var res = await fetch(REST_URL, { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json" }, credentials: "same-origin", body: JSON.stringify(payload), }); if (!res.ok) { fareCalCache[cacheKey] = null; return null; } data = await res.json(); } else { var params = new URLSearchParams({ engine: "google_flights", api_key: API_KEY, departure_id: state.from, arrival_id: state.to, outbound_date: outDate, flight_type: apiTrip, travel_class: state.travelClass, adults: String(parseInt(state.adults, 10) || 1), children: String(parseInt(state.children, 10) || 0), stops: "any", sort_by: "price", currency: state.currency, gl: "in", hl: "en", }); if (apiTrip === "round_trip" && retDate) params.set("return_date", retDate); var resLegacy = await fetch(BASE_URL + "?" + params.toString()); if (!resLegacy.ok) { fareCalCache[cacheKey] = null; return null; } data = await resLegacy.json(); } var lowest = parseLowestPriceFromData(data); fareCalCache[cacheKey] = lowest; return lowest; } catch (e) { fareCalCache[cacheKey] = null; return null; } } function syncResultsUrl() { if (!isResults || !RESULTS_PAGE_URL) return; var sep = RESULTS_PAGE_URL.indexOf("?") >= 0 ? "&" : "?"; var nextUrl = RESULTS_PAGE_URL + sep + buildTfQuery(); if (window.history && window.history.replaceState) { window.history.replaceState({}, "", nextUrl); } } async function renderFareCalendar() { if (!isResults || fareCalLoading) return; var host = document.getElementById("travelgo-fr-farecal-track"); if (!host || !state.departDate) return; fareCalLoading = true; var nights = 0; if (state.tripType === "round_trip" && state.returnDate) { nights = Math.max(1, diffDaysIso(state.departDate, state.returnDate)); } var days = []; for (var i = -3; i <= 3; i++) { days.push(addDaysIso(state.departDate, i)); } host.innerHTML = days .map(function (d) { return ( '' ); }) .join(""); var jobs = days.map(async function (d) { var rd = state.tripType === "round_trip" && nights > 0 ? addDaysIso(d, nights) : ""; var p = await fetchFareForDate(d, rd); return { d: d, p: p }; }); var rows = await Promise.all(jobs); rows.forEach(function (row) { var chip = host.querySelector('[data-fare-date="' + row.d + '"]'); if (!chip) return; var priceEl = chip.querySelector(".travelgo-fr-farecal-price"); if (priceEl) { priceEl.textContent = row.p != null ? formatMoney(row.p) : "N/A"; } }); host.querySelectorAll(".travelgo-fr-farecal-chip").forEach(function (chip) { chip.addEventListener("click", function () { var d = chip.getAttribute("data-fare-date"); if (!d || d === state.departDate) return; var oldDep = state.departDate; state.departDate = d; if (state.tripType === "round_trip" && state.returnDate) { var span = Math.max(1, diffDaysIso(oldDep, state.returnDate)); state.returnDate = addDaysIso(d, span); } host.querySelectorAll(".travelgo-fr-farecal-chip").forEach(function (c) { c.classList.toggle("is-active", c.getAttribute("data-fare-date") === d); }); syncResultsUrl(); fetchFlightData(); renderFareCalendar(); }); }); fareCalLoading = false; } function updatePaxSummary() { var total = parseInt(state.adults, 10) + parseInt(state.children, 10) + parseInt(state.infants, 10); var ps = $("travelgo-ss-pax-summary"); var cs = $("travelgo-ss-class-summary"); if (ps) { ps.textContent = total === 1 ? S.oneTraveller || "1 Traveller" : total + " " + (S.travellers || "Travellers"); } if (cs) { cs.textContent = classLabels[state.travelClass] || state.travelClass; } } function initDates() { var today = new Date(); var depart = new Date(today); depart.setDate(today.getDate() + 15); var ret = new Date(depart); ret.setDate(depart.getDate() + 7); function fmt(d) { return d.toISOString().split("T")[0]; } var depEl = $("travelgo-ss-depart-date"); var retEl = $("travelgo-ss-return-date"); if (!depEl || !retEl) return; depEl.value = fmt(depart); retEl.value = fmt(ret); depEl.min = fmt(today); retEl.min = fmt(depart); updateDateDisplays(); } function updateDateDisplays() { var depEl = $("travelgo-ss-depart-date"); var retEl = $("travelgo-ss-return-date"); var dv = depEl ? depEl.value : ""; var rv = retEl ? retEl.value : ""; if (dv) { state.departDate = dv; if (retEl) retEl.min = dv; } if (rv) { state.returnDate = rv; } } function updateAirport(field, code) { var upperCode = String(code || "") .toUpperCase() .replace(/[^A-Z]/g, "") .substring(0, 3); if (upperCode.length < 3) return; if (field === "from") { state.from = upperCode; var fd = $("travelgo-ss-from-display"); var fm = $("travelgo-ss-from-meta"); var fi = $("travelgo-ss-from-input"); if (fd) fd.textContent = cityLine(upperCode); if (fm) fm.textContent = metaLine(upperCode); if (fi) { fi.value = upperCode; fi.setAttribute("data-selected-code", upperCode); } } else { state.to = upperCode; var td = $("travelgo-ss-to-display"); var tm = $("travelgo-ss-to-meta"); var ti = $("travelgo-ss-to-input"); if (td) td.textContent = cityLine(upperCode); if (tm) tm.textContent = metaLine(upperCode); if (ti) { ti.value = upperCode; ti.setAttribute("data-selected-code", upperCode); } } } function wireAirportAutocomplete(inputId, ddId, field) { var el = $(inputId); var dd = $(ddId); if (!el || !dd) return; var fieldsRow = $("travelgo-ss-fields-row"); var loadingTimer = null; var requestToken = 0; function closeDd() { if (loadingTimer) { clearTimeout(loadingTimer); loadingTimer = null; } dd.hidden = true; dd.innerHTML = ""; if (fieldsRow) fieldsRow.classList.remove("travelgo-gb-bar--dd-open"); } function renderDdLoading() { dd.innerHTML = '
' + '' + '' + escapeHtml(S.searching || "Searching…") + "
"; dd.hidden = false; if (fieldsRow) fieldsRow.classList.add("travelgo-gb-bar--dd-open"); } function renderDd(items) { if (!items.length) { closeDd(); return; } dd.innerHTML = items .map(function (a) { return ( '" ); }) .join(""); dd.hidden = false; if (fieldsRow) fieldsRow.classList.add("travelgo-gb-bar--dd-open"); } function pickAirport(iata) { if (!iata) return; updateAirport(field, iata); closeDd(); if (document.activeElement === el) el.blur(); } function validateSelection() { var selected = el.getAttribute("data-selected-code") || ""; if (selected && selected.length === 3) { updateAirport(field, selected); return true; } var cur = field === "from" ? state.from : state.to; updateAirport(field, cur); return false; } el.addEventListener("focus", function () { requestToken++; var myToken = requestToken; renderDdLoading(); Promise.resolve(fetchAirportSuggestionsRemote(el.value)).then(function () { if (myToken !== requestToken) return; renderDd(airportSuggestions(el.value, 10)); }); }); el.addEventListener("input", function () { el.removeAttribute("data-selected-code"); renderDdLoading(); if (loadingTimer) clearTimeout(loadingTimer); loadingTimer = setTimeout(function () { loadingTimer = null; requestToken++; var myToken = requestToken; Promise.resolve(fetchAirportSuggestionsRemote(el.value)).then(function () { if (myToken !== requestToken) return; renderDd(airportSuggestions(el.value, 10)); }); }, 120); }); dd.addEventListener("mousedown", function (e) { var btn = e.target.closest(".travelgo-gb-airport-opt"); if (!btn || !dd.contains(btn)) return; // Select on mousedown so blur doesn't reset to the previous airport. e.preventDefault(); var iata = btn.getAttribute("data-iata"); pickAirport(iata); }); dd.addEventListener("click", function (e) { var btn = e.target.closest(".travelgo-gb-airport-opt"); if (!btn || !dd.contains(btn)) return; var iata = btn.getAttribute("data-iata"); pickAirport(iata); }); el.addEventListener("blur", function () { setTimeout(function () { validateSelection(); closeDd(); }, 120); }); document.addEventListener("click", function (e) { if (e.target === el || dd.contains(e.target)) return; closeDd(); }); } function setTripType(type) { if (!homeRoot) return; state.tripType = type; homeRoot.classList.toggle("travelgo-trip-one-way", type === "one_way"); homeRoot.classList.toggle("travelgo-trip-multi-city", type === "multi_city"); homeRoot.querySelectorAll(".travelgo-gb-trip-opt").forEach(function (b) { var on = b.getAttribute("data-type") === type; b.classList.toggle("is-active", on); b.setAttribute("aria-selected", on ? "true" : "false"); var ch = b.querySelector(".travelgo-gb-trip-check"); if (ch) ch.textContent = on ? "✓" : ""; }); var rf = $("travelgo-ss-return-field"); var msg = $("travelgo-gb-multicity-msg"); var searchBtn = $("travelgo-ss-search"); if (msg) msg.hidden = type !== "multi_city"; if (rf) rf.hidden = type === "one_way" || type === "multi_city"; if (type === "one_way" || type === "multi_city") { var retDate = $("travelgo-ss-return-date"); if (retDate) retDate.value = ""; state.returnDate = ""; } if (searchBtn) searchBtn.disabled = type === "multi_city"; } if (isHome && homeRoot) { var hmCur = $("travelgo-ss-currency"); state.currency = normalizeCurrency((hmCur && hmCur.value) || (cfg.defaultCurrency || "INR")); if (hmCur) { hmCur.value = state.currency; hmCur.addEventListener("change", function () { state.currency = normalizeCurrency(hmCur.value); }); } homeRoot.querySelectorAll(".travelgo-gb-trip-opt").forEach(function (btn) { btn.addEventListener("click", function () { var type = btn.getAttribute("data-type") || "round_trip"; setTripType(type); }); }); wireAirportAutocomplete("travelgo-ss-from-input", "travelgo-ss-from-dd", "from"); wireAirportAutocomplete("travelgo-ss-to-input", "travelgo-ss-to-dd", "to"); var swapBtn = $("travelgo-ss-swap"); if (swapBtn) swapBtn.addEventListener("click", function () { var tmp = state.from; state.from = state.to; state.to = tmp; updateAirport("from", state.from); updateAirport("to", state.to); }); var depDate = $("travelgo-ss-depart-date"); var retDate = $("travelgo-ss-return-date"); if (depDate) depDate.addEventListener("change", updateDateDisplays); if (retDate) retDate.addEventListener("change", updateDateDisplays); var retClear = $("travelgo-ss-return-clear"); if (retClear) retClear.addEventListener("click", function (e) { e.preventDefault(); e.stopPropagation(); if (retDate) { retDate.value = ""; state.returnDate = ""; } }); function fillNumRow(containerId, min, max, current) { var el = $(containerId); if (!el) return; el.innerHTML = ""; var cur = parseInt(current, 10); for (var i = min; i <= max; i++) { var b = document.createElement("button"); b.type = "button"; b.className = "travelgo-gb-num-btn" + (i === cur ? " is-active" : ""); b.setAttribute("data-value", String(i)); b.textContent = String(i); el.appendChild(b); } } function wireNumRow(containerId, field) { var el = $(containerId); if (!el) return; el.addEventListener("click", function (e) { var t = e.target.closest(".travelgo-gb-num-btn"); if (!t) return; var v = t.getAttribute("data-value"); popoverDraft[field] = v; el.querySelectorAll(".travelgo-gb-num-btn").forEach(function (btn) { btn.classList.toggle("is-active", btn.getAttribute("data-value") === v); }); }); } var pop = $("travelgo-gb-popover"); var backdrop = $("travelgo-gb-popover-backdrop"); var paxTrigger = $("travelgo-gb-pax-trigger"); function openPopover() { popoverDraft.adults = state.adults; popoverDraft.children = state.children; popoverDraft.infants = state.infants; popoverDraft.travelClass = state.travelClass; popoverDraft.stops = state.stops; fillNumRow("travelgo-gb-adults-row", 1, 9, popoverDraft.adults); fillNumRow("travelgo-gb-children-row", 0, 6, popoverDraft.children); fillNumRow("travelgo-gb-infants-row", 0, 6, popoverDraft.infants); homeRoot.querySelectorAll(".travelgo-gb-class-btn").forEach(function (b) { var cl = b.getAttribute("data-class"); b.classList.toggle("is-active", cl === popoverDraft.travelClass); }); homeRoot.querySelectorAll(".travelgo-gb-stop-btn").forEach(function (b) { var st = b.getAttribute("data-stops"); b.classList.toggle("is-active", st === popoverDraft.stops); }); if (pop) { pop.hidden = false; } if (backdrop) { backdrop.hidden = false; } if (paxTrigger) { paxTrigger.setAttribute("aria-expanded", "true"); } } function closePopover() { if (pop) pop.hidden = true; if (backdrop) backdrop.hidden = true; if (paxTrigger) paxTrigger.setAttribute("aria-expanded", "false"); } wireNumRow("travelgo-gb-adults-row", "adults"); wireNumRow("travelgo-gb-children-row", "children"); wireNumRow("travelgo-gb-infants-row", "infants"); homeRoot.querySelectorAll(".travelgo-gb-class-btn").forEach(function (b) { b.addEventListener("click", function () { var cl = b.getAttribute("data-class") || "economy"; popoverDraft.travelClass = cl; homeRoot.querySelectorAll(".travelgo-gb-class-btn").forEach(function (x) { x.classList.toggle("is-active", x.getAttribute("data-class") === cl); }); }); }); homeRoot.querySelectorAll(".travelgo-gb-stop-btn").forEach(function (b) { b.addEventListener("click", function () { var st = b.getAttribute("data-stops") || "any"; popoverDraft.stops = st; homeRoot.querySelectorAll(".travelgo-gb-stop-btn").forEach(function (x) { x.classList.toggle("is-active", x.getAttribute("data-stops") === st); }); }); }); if (paxTrigger) { paxTrigger.addEventListener("click", function (e) { e.preventDefault(); openPopover(); }); } if (backdrop) { backdrop.addEventListener("click", closePopover); } var applyBtn = $("travelgo-gb-pax-apply"); if (applyBtn) { applyBtn.addEventListener("click", function () { state.adults = popoverDraft.adults; state.children = popoverDraft.children; state.infants = popoverDraft.infants; state.travelClass = popoverDraft.travelClass; state.stops = popoverDraft.stops; updatePaxSummary(); closePopover(); }); } homeRoot.querySelectorAll(".travelgo-gb-fare-opt").forEach(function (b) { b.addEventListener("click", function () { var fare = b.getAttribute("data-fare") || "regular"; state.fareType = fare; homeRoot.querySelectorAll(".travelgo-gb-fare-opt").forEach(function (x) { x.classList.toggle("is-active", x.getAttribute("data-fare") === fare); }); }); }); } function apiSortBy() { if (state.sortBy === "popularity") return "top_flights"; return state.sortBy; } function sortFlightsCopy(flights) { return flights.slice().sort(function (a, b) { var stopsA = stopCountFlight(a); var stopsB = stopCountFlight(b); if (stopsA !== stopsB) return stopsA - stopsB; if (state.sortBy === "price") return (a.price || 0) - (b.price || 0); if (state.sortBy === "duration") return (a.total_duration || 0) - (b.total_duration || 0); if (state.sortBy === "popularity") { var ra = Number(a.rating || 0); var rb = Number(b.rating || 0); var ba = Number(a.booking_today || 0); var bb = Number(b.booking_today || 0); if (!isFinite(ra)) ra = 0; if (!isFinite(rb)) rb = 0; var sa = ra * 20 + Math.min(35, ba / 2); var sb = rb * 20 + Math.min(35, bb / 2); if (Math.abs(sb - sa) > 0.01) return sb - sa; return (a.price || 0) - (b.price || 0); } if (state.sortBy === "top_flights") { var pa = a._travelgo_policy && typeof a._travelgo_policy.score === "number" ? a._travelgo_policy.score : 0; var pb = b._travelgo_policy && typeof b._travelgo_policy.score === "number" ? b._travelgo_policy.score : 0; if (Math.abs(pb - pa) > 0.01) return pb - pa; return (a.price || 0) - (b.price || 0); } if (state.sortBy === "departure_time") { var ta = a.flights && a.flights[0] && a.flights[0].departure_airport ? a.flights[0].departure_airport.time || "" : ""; var tb = b.flights && b.flights[0] && b.flights[0].departure_airport ? b.flights[0].departure_airport.time || "" : ""; return ta.localeCompare(tb); } return 0; }); } function bindSortContainer(container) { if (!container) return; container.addEventListener("click", function (e) { var pill = e.target.closest(".travelgo-ss-sort-pill"); if (!pill || !container.contains(pill)) return; container.querySelectorAll(".travelgo-ss-sort-pill").forEach(function (p) { p.classList.remove("is-active"); }); pill.classList.add("is-active"); state.sortBy = pill.getAttribute("data-sort") || "top_flights"; syncSortUi(); if (!state.allFlights.length) return; if (isResults) applyFiltersAndRender(); }); } if (isResults && resultsRoot) { bindSortContainer(resultsRoot.querySelector("#travelgo-ss-sort-pills")); } function stopCountFlight(f) { if ( typeof f.number_of_stops === "number" && !isNaN(f.number_of_stops) && f.number_of_stops >= 0 ) { return f.number_of_stops; } return (f.flights && f.flights.length ? f.flights.length : 1) - 1; } function departureHour(f) { var t = f.flights && f.flights[0] && f.flights[0].departure_airport && f.flights[0].departure_airport.time; if (!t) return null; var parts = String(t).split(":"); var h = parseInt(parts[0], 10); return isNaN(h) ? null : h; } function anyStopFilterOn() { return ( filterState.stopsNonstop || filterState.stopsOne || filterState.stopsTwoPlus ); } function anyDepFilterOn() { return ( filterState.depBefore6 || filterState.dep6to12 || filterState.dep12to18 || filterState.depAfter18 ); } function matchesStopFilters(f) { if (!anyStopFilterOn()) return true; var sc = stopCountFlight(f); if (filterState.stopsNonstop && sc === 0) return true; if (filterState.stopsOne && sc === 1) return true; if (filterState.stopsTwoPlus && sc >= 2) return true; return false; } function matchesDepFilters(f) { if (!anyDepFilterOn()) return true; var h = departureHour(f); if (h === null) return true; if (filterState.depBefore6 && h < 6) return true; if (filterState.dep6to12 && h >= 6 && h < 12) return true; if (filterState.dep12to18 && h >= 12 && h < 18) return true; if (filterState.depAfter18 && h >= 18) return true; return false; } function matchesPriceFilter(f) { if (filterState.priceMaxUser == null) return true; var p = f.price || 0; return p <= filterState.priceMaxUser; } function matchesAirlineFilter(f) { if (!filterState.airlinesAllowed) return true; var name = (f.flights && f.flights[0] && f.flights[0].airline) || ""; return filterState.airlinesAllowed.indexOf(name) !== -1; } function flightMatchesFilters(f) { return ( matchesStopFilters(f) && matchesDepFilters(f) && matchesPriceFilter(f) && matchesAirlineFilter(f) ); } function getFilteredFlights() { if (!state.allFlights.length) return []; return state.allFlights.filter(flightMatchesFilters); } function minPriceWhere(pred) { var m = Infinity; state.allFlights.forEach(function (f) { if (pred(f)) m = Math.min(m, f.price || 0); }); return m === Infinity ? null : m; } function updateFilterHints() { var fp = S.fromPrice || "from"; var h0 = $("travelgo-fr-hint-nonstop"); var h1 = $("travelgo-fr-hint-1stop"); var h2 = $("travelgo-fr-hint-2plus"); var mn0 = minPriceWhere(function (f) { return stopCountFlight(f) === 0; }); var mn1 = minPriceWhere(function (f) { return stopCountFlight(f) === 1; }); var mn2 = minPriceWhere(function (f) { return stopCountFlight(f) >= 2; }); function setHint(el, val) { if (!el) return; el.textContent = val != null ? fp + " " + formatMoney(val) : ""; } setHint(h0, mn0); setHint(h1, mn1); setHint(h2, mn2); } function syncPriceSliderFromData() { var el = $("travelgo-fr-price-slider"); var minL = $("travelgo-fr-price-min-lbl"); var maxL = $("travelgo-fr-price-max-lbl"); var cur = $("travelgo-fr-price-current"); if (!el || !state.allFlights.length) return; var prices = state.allFlights.map(function (f) { return f.price || 0; }); var mn = Math.min.apply(null, prices); var mx = Math.max.apply(null, prices); el.min = String(Math.max(0, mn - (mn % 100))); el.max = String(mx); el.step = mx - mn > 5000 ? "500" : "100"; filterState.priceMaxUser = mx; el.value = String(mx); if (minL) minL.textContent = formatMoney(mn); if (maxL) maxL.textContent = formatMoney(mx); if (cur) { cur.textContent = (S.maxPriceLabel || "Showing up to") + " " + formatMoney(mx); } } function syncAirlineFilterFromDom() { var host = $("travelgo-fr-airlines"); if (!host) return; var cbs = host.querySelectorAll(".travelgo-fr-al-cb"); var checked = []; var n = 0; cbs.forEach(function (cb) { n++; if (cb.checked) checked.push(cb.getAttribute("data-airline") || ""); }); filterState.airlinesAllowed = checked.length === n ? null : checked; } var OBSOLETE_AIRLINE_RE = /air\s*sahara|air\s*pegasus|go\s*air\b|eurocypria|astral\s*aviation|kingfisher|jet\s*airways|go\s*first|air\s*deccan|modi\s*luft/i; function rebuildAirlineFilters(flights) { var host = $("travelgo-fr-airlines"); if (!host) return; var names = {}; flights.forEach(function (f) { var n = (f.flights && f.flights[0] && f.flights[0].airline) || ""; if (n && !OBSOLETE_AIRLINE_RE.test(String(n))) names[n] = true; }); var list = Object.keys(names).sort(); if (!list.length) { host.innerHTML = '

' + escapeHtml(S.noAirlines || "—") + "

"; return; } host.innerHTML = list .map(function (name, i) { var sid = "travelgo-fr-al-" + i; var a = String(name) .replace(/&/g, "&") .replace(/"/g, """) .replace(/' + '' + '' + escapeHtml(name) + "" ); }) .join(""); filterState.airlinesAllowed = null; } function clearAllFiltersUi() { filterState.stopsNonstop = filterState.stopsOne = filterState.stopsTwoPlus = false; filterState.depBefore6 = filterState.dep6to12 = filterState.dep12to18 = filterState.depAfter18 = false; var n1 = $("travelgo-fr-f-nonstop"); var n2 = $("travelgo-fr-f-1stop"); var n3 = $("travelgo-fr-f-2plus"); if (n1) n1.checked = false; if (n2) n2.checked = false; if (n3) n3.checked = false; ROOT.querySelectorAll(".travelgo-fr-time-tile").forEach(function (b) { b.classList.remove("is-selected"); }); var host = $("travelgo-fr-airlines"); if (host) { host.querySelectorAll(".travelgo-fr-al-cb").forEach(function (cb) { cb.checked = true; }); } filterState.airlinesAllowed = null; syncPriceSliderFromData(); } function applyFiltersAndRender() { var filtered = getFilteredFlights(); state.currentResults = filtered; var countEl = res$("results-count"); if (countEl && state.allFlights.length) { countEl.textContent = filtered.length + " " + (filtered.length === 1 ? S.flightSingular || "flight" : S.flightPlural || "flights") + " " + (S.found || "found"); } renderFlights(filtered, state.currentMeta); } function wireResultsFilters() { if (!isResults || filtersWired) return; var sidebar = $("travelgo-fr-sidebar"); if (!sidebar) return; filtersWired = true; sidebar.addEventListener("change", function (ev) { filterState.stopsNonstop = !!( $("travelgo-fr-f-nonstop") && $("travelgo-fr-f-nonstop").checked ); filterState.stopsOne = !!( $("travelgo-fr-f-1stop") && $("travelgo-fr-f-1stop").checked ); filterState.stopsTwoPlus = !!( $("travelgo-fr-f-2plus") && $("travelgo-fr-f-2plus").checked ); var t = ev.target; if (t && t.classList && t.classList.contains("travelgo-fr-al-cb")) { syncAirlineFilterFromDom(); } if (state.allFlights.length) applyFiltersAndRender(); }); var slider = $("travelgo-fr-price-slider"); if (slider) { slider.addEventListener("input", function () { filterState.priceMaxUser = parseInt(slider.value, 10) || 0; var cur = $("travelgo-fr-price-current"); if (cur) { cur.textContent = (S.maxPriceLabel || "Showing up to") + " " + formatMoney(filterState.priceMaxUser); } if (state.allFlights.length) applyFiltersAndRender(); }); } sidebar.addEventListener("click", function (e) { var btn = e.target.closest(".travelgo-fr-time-tile"); if (!btn || !sidebar.contains(btn)) return; var dep = btn.getAttribute("data-fr-dep"); if (!dep) return; var map = { before6: "depBefore6", m6_12: "dep6to12", m12_18: "dep12to18", after18: "depAfter18", }; var key = map[dep]; if (!key) return; filterState[key] = !filterState[key]; btn.classList.toggle("is-selected", filterState[key]); if (state.allFlights.length) applyFiltersAndRender(); }); var clr = $("travelgo-fr-clear-filters"); if (clr) { clr.addEventListener("click", function () { clearAllFiltersUi(); if (state.allFlights.length) applyFiltersAndRender(); }); } } function renderSkeletons() { return Array(4) .fill("") .map(function () { return ( '
' + '
' + '
' + '
' + '
' + "
" + '
' + "
" ); }) .join(""); } /** City / area label under times (from airport name or IATA). */ function airportCityFromAp(ap) { if (!ap || typeof ap !== "object") return ""; var name = ap.name ? String(ap.name) : ""; if (name) { var s = name .replace(/\s+International Airport/gi, "") .replace(/\s+Airport/gi, "") .trim(); var cut = s.split(",")[0].trim(); if (cut) return cut; } return ap.id ? String(ap.id) : ""; } /** Official airline logos (Kiwi CDN) first; theme bundle only as fallback. */ function extractIataFromFlightNum(seg) { var raw = String(seg && seg.flight_number ? seg.flight_number : "").trim(); if (!raw) return ""; var compact = raw.toUpperCase().replace(/[\s\-_]/g, ""); if (compact.length >= 3 && /^[A-Z0-9]{2}\d/.test(compact)) { return compact.slice(0, 2); } var m = raw.toUpperCase().match(/^([A-Z0-9]{2})\s*\d/); return m ? m[1] : ""; } function icaoFromSegment(seg) { if (!seg || typeof seg !== "object") return ""; var iata = extractIataFromFlightNum(seg); var nm = String(seg.airline || "").toLowerCase(); if (iata === "6E" || nm.indexOf("indigo") !== -1) return "IGO"; if (iata === "AI" || (nm.indexOf("air india") !== -1 && nm.indexOf("express") === -1)) return "AIC"; if (iata === "IX" || nm.indexOf("air india express") !== -1) return "AXB"; if (iata === "UK" || nm.indexOf("vistara") !== -1) return "VTI"; if (iata === "SG" || nm.indexOf("spicejet") !== -1) return "SEJ"; if (iata === "QP" || nm.indexOf("akasa") !== -1) return "AKJ"; if (iata === "G8" || nm.indexOf("go air") !== -1 || nm.indexOf("goair") !== -1) return "GOW"; return ""; } function hasBundledAirlineLogo(icao) { if (!icao) return false; return BUNDLED_AIRLINE_ICAO.indexOf(icao) !== -1; } function bundledAirlineLogoUrl(icao) { if (!AIRLINE_LOGOS_URI || !icao || !hasBundledAirlineLogo(icao)) return ""; var q = ASSET_VERSION ? "?v=" + encodeURIComponent(ASSET_VERSION) : ""; return AIRLINE_LOGOS_URI + "/" + icao + ".svg" + q; } function remoteAirlineLogoUrl(iata) { if (!iata || iata.length !== 2) return ""; if (!REMOTE_AIRLINE_LOGO_URL || REMOTE_AIRLINE_LOGO_URL.indexOf("%s") === -1) return ""; return REMOTE_AIRLINE_LOGO_URL.replace("%s", encodeURIComponent(iata)); } function resolveAirlineLogoPair(seg) { var apiLogo = String((seg && seg.airline_logo) || "").trim(); var iata = extractIataFromFlightNum(seg); var icao = icaoFromSegment(seg); var bundled = bundledAirlineLogoUrl(icao); var remote = remoteAirlineLogoUrl(iata); var primary = remote || apiLogo || bundled; var fallback = ""; if (primary === remote) { fallback = apiLogo && apiLogo !== remote ? apiLogo : bundled; } else if (primary === apiLogo) { fallback = remote && remote !== apiLogo ? remote : bundled; } else if (primary === bundled) { fallback = remote || apiLogo; } if (fallback && fallback === primary) fallback = ""; return { primary: primary, fallback: fallback }; } function airlineLogoImgHtml(seg, airlineName) { var logos = resolveAirlineLogoPair(seg); if (!logos.primary) return "✈"; var isBundledWide = logos.primary.indexOf("kiwi.com") === -1 && logos.primary.indexOf("searchapi") === -1 && (logos.primary.indexOf("/AIC.") !== -1 || logos.primary.indexOf("/AXB.") !== -1); var wideClass = isBundledWide ? " travelgo-ss-airline-logo-img--wide" : ""; var fb = logos.fallback ? logos.fallback.replace(/"/g, """) : ""; return ( '' +
      escapeHtml(airlineName || ' ); } function resolveBundledAirlineLogo(seg) { return bundledAirlineLogoUrl(icaoFromSegment(seg)); } function buildFlightCard(f, idx) { var firstFlight = f.flights && f.flights[0] ? f.flights[0] : {}; var lastIdx = f.flights && f.flights.length ? f.flights.length - 1 : 0; var lastFlight = f.flights && f.flights[lastIdx] ? f.flights[lastIdx] : {}; var depAirport = firstFlight.departure_airport || {}; var arrAirport = lastFlight.arrival_airport || {}; var policyNotes = f._travelgo_policy && Array.isArray(f._travelgo_policy.notes) ? f._travelgo_policy.notes : []; var mins = f.total_duration || 0; var durStr = Math.floor(mins / 60) + " h " + (mins % 60) + " m"; var stopCount = typeof f.number_of_stops === "number" && !isNaN(f.number_of_stops) && f.number_of_stops >= 0 ? f.number_of_stops : (f.flights && f.flights.length ? f.flights.length : 1) - 1; var viaLabel = ""; if (stopCount > 0) { if (f.layovers && f.layovers[0]) { viaLabel = String(f.layovers[0].name || f.layovers[0].id || ""); viaLabel = viaLabel.replace(/\s+International Airport/gi, "").split(",")[0].trim(); } else if (f.flights && f.flights.length > 1) { viaLabel = airportCityFromAp(f.flights[0].arrival_airport || {}); } } var stopsLabelRaw = stopCount === 0 ? S.nonstopLabel || "Non stop" : stopCount === 1 ? (S.stopViaOne || "1 stop via") + " " + (viaLabel || S.viaUnknown || "connection") : stopCount + " " + (S.stopsMany || "stops"); var stopsClass = stopCount === 0 ? "travelgo-ss-nonstop" : ""; var price = f.price || 0; var pricing = f._travelgoPricing || null; var isOvernightAny = f.flights && f.flights.some(function (x) { return x.is_overnight; }); var isDelayedAny = f.flights && f.flights.some(function (x) { return x.is_often_delayed; }); var airlineName = firstFlight.airline || ""; var flightNum = firstFlight.flight_number || ""; var plane = firstFlight.airplane || ""; var allFlightNums = (f.flights || []) .map(function (s) { return s && s.flight_number ? String(s.flight_number).trim() : ""; }) .filter(Boolean) .join(", "); if (!allFlightNums) { allFlightNums = flightNum; } var depTime = formatClockTime(depAirport.time); var arrTime = formatClockTime(arrAirport.time); var depCode = depAirport.id || state.from; var arrCode = arrAirport.id || state.to; var depCity = airportCityFromAp(depAirport); var arrCity = airportCityFromAp(arrAirport); var priceNotes = []; if (pricing && pricing.convenienceFee > 0) { priceNotes.push( "Incl. conv. fee " + formatMoney(pricing.convenienceFee) ); } if (pricing && pricing.couponAmount > 0) { priceNotes.push("Coupon up to " + formatMoney(pricing.couponAmount)); } var segRows = ""; if (f.flights && f.flights.length) { var meta = state.currentMeta && state.currentMeta._travelgo_search; var outFromMeta = meta && meta.outbound_date ? String(meta.outbound_date) : ""; var retFromMeta = meta && meta.return_date ? String(meta.return_date) : ""; var implicitDate = normalizeSearchDateToIso(state.departDate || "") || normalizeSearchDateToIso(outFromMeta) || ""; var normalizedReturn = normalizeSearchDateToIso(state.returnDate || "") || normalizeSearchDateToIso(retFromMeta) || ""; var retLegStart = -1; if (state.tripType === "round_trip" && normalizedReturn && state.from && state.to) { for (var rj = 0; rj < f.flights.length; rj++) { var sf = f.flights[rj]; var did = sf.departure_airport && sf.departure_airport.id ? String(sf.departure_airport.id).toUpperCase() : ""; if (did === String(state.to).toUpperCase()) { retLegStart = rj; break; } } } segRows = f.flights .map(function (seg, si) { if (retLegStart >= 0 && si === retLegStart && normalizedReturn) { implicitDate = normalizedReturn; } var depAp = seg.departure_airport || {}; var arrAp = seg.arrival_airport || {}; var depParsed = parseAirportDateTime(depAp.time); var depLabel = depParsed.iso !== "" ? formatDateWithTime(depParsed.iso, depParsed.clock) : formatDateWithTime(implicitDate, depAp.time); var depDayIso = normalizeSearchDateToIso( depParsed.iso !== "" ? depParsed.iso : implicitDate ); var arrParsed = parseAirportDateTime(arrAp.time); var arrDayIso; if (arrParsed.iso !== "") { arrDayIso = normalizeSearchDateToIso(arrParsed.iso); } else if (depDayIso) { arrDayIso = depDayIso; if (seg.is_overnight) { arrDayIso = addDaysIso(depDayIso, 1); } else { var dm = clockToMins(depAp.time); var am = clockToMins(arrAp.time); if (dm !== null && am !== null && am < dm) { arrDayIso = addDaysIso(depDayIso, 1); } } } else { arrDayIso = ""; } var arrLabel = arrParsed.iso !== "" ? formatDateWithTime(arrParsed.iso, arrParsed.clock) : formatDateWithTime(arrDayIso, arrAp.time); implicitDate = normalizeSearchDateToIso( arrParsed.iso !== "" ? arrParsed.iso : arrDayIso ) || implicitDate; return ( '
' + '
' + '
' + (depAp.id ? String(depAp.id) : "") + " " + depLabel + " → " + (arrAp.id ? String(arrAp.id) : "") + " " + arrLabel + "" + (seg.airline || "") + " " + (seg.flight_number || "") + " · " + Math.floor((seg.duration || 0) / 60) + "h " + ((seg.duration || 0) % 60) + "m
" ); }) .join(""); } var layoverRows = f.layovers && f.layovers.length ? f.layovers .map(function (lay) { return ( '
' + "⏱ " + Math.floor(lay.duration / 60) + "h " + (lay.duration % 60) + "m " + (S.layoverAt || "layover at") + " " + (lay.name || "") + " (" + (lay.id || "") + ")" + "
" ); }) .join("") : ""; var policyHtml = policyNotes.length > 0 ? '
' + policyNotes .map(function (n) { return ( '' + escapeHtml(String(n)) + "" ); }) .join("") + "
" : ""; var ribL = ""; if ( policyNotes.some(function (n) { return /meal|dining|food|breakfast/i.test(String(n)); }) ) { ribL = S.freeMealRibbon || "Meal included on select fares"; } else if (/air india(?!\s+express)/i.test(airlineName)) { ribL = S.freeMealRibbon || "Meal on long routes · confirm with airline"; } var ribR = !isDelayedAny ? String(80 + ((idx * 5) % 19)) + "% " + (S.onTimeRibbon || "on time") : S.delaysPossible || "Allow time for connections"; var ribbonHtml = ribL || ribR ? '
' + (ribL ? '' + escapeHtml(ribL) + "" : "") + (ribR ? '' + escapeHtml(ribR) + "" : "") + "
" : ""; var overnightTag = isOvernightAny ? '' + escapeHtml(S.plusOneDay || "+1 DAY") + "" : ""; var lockFee = Math.max(49, Math.round(Number(price) * 0.033)); var lockLine = String(S.lockPriceFor || "Lock this price for %s").replace( "%s", formatMoney(lockFee) ); var promoText = S.promoStripDefault || "Bank & UPI offers at checkout · fare shown is indicative — confirm on booking page."; var metaHiddenBits = []; if (plane) metaHiddenBits.push(escapeHtml(plane)); if (isDelayedAny) metaHiddenBits.push(escapeHtml(S.oftenDelayed || "Often delayed")); var metaHidden = metaHiddenBits.length > 0 ? '

' + metaHiddenBits.join(" · ") + "

" : ""; return ( '
' + ribbonHtml + '
' + '
' + '" + '
' + '
' + escapeHtml(airlineName) + "
" + '
' + escapeHtml(allFlightNums) + '
" + '
' + '
' + escapeHtml(depTime) + '
' + escapeHtml(depCity || depCode) + '
' + escapeHtml(depCode) + "
" + '
' + '
' + escapeHtml(durStr) + '
' + '
' + escapeHtml(stopsLabelRaw) + "
" + '
' + '
' + escapeHtml(arrTime) + overnightTag + '
' + escapeHtml(arrCity || arrCode) + '
' + escapeHtml(arrCode) + "
" + '
' + '
' + currencySymbol(state.currency) + "" + Number(price).toLocaleString(currencyLocale(state.currency)) + "
" + '
' + escapeHtml(S.perAdult || S.perPerson || "per adult") + "
" + '
' + escapeHtml(priceNotes.join(" · ")) + "
" + '" + '
" + layoverRows + '" + '
" ); } function escapeHtml(s) { return String(s) .replace(/&/g, "&") .replace(/

" + (S.noFilterMatch || "No flights match your filters. Try clearing filters.") + "

"; return; } list.innerHTML = sorted.map(function (f, i) { return buildFlightCard(f, i); }).join(""); state.lastRenderedFlights = sorted; list.querySelectorAll(".travelgo-ss-expand").forEach(function (t) { t.addEventListener("click", function () { var card = t.closest(".travelgo-ss-flight-card"); var seg = card ? card.querySelector(".travelgo-ss-segments") : null; var tx = t.querySelector(".travelgo-ss-expand-text"); if (!card || !seg) return; seg.hidden = !seg.hidden; t.setAttribute("aria-expanded", seg.hidden ? "false" : "true"); if (tx) { var openLab = S.viewFlightDetails || S.viewDetails || "View details"; tx.textContent = seg.hidden ? openLab : S.hideDetails || "Hide details"; } }); }); list.querySelectorAll(".travelgo-ss-compare-btn").forEach(function (b) { b.addEventListener("click", function (e) { e.preventDefault(); e.stopPropagation(); }); }); list.querySelectorAll(".travelgo-ss-lock-btn").forEach(function (b) { b.addEventListener("click", function (e) { e.preventDefault(); e.stopPropagation(); }); }); list.querySelectorAll(".travelgo-ss-book-btn").forEach(function (b) { b.addEventListener("click", function (e) { e.stopPropagation(); if (!BOOKING_ENABLED || !AJAX_URL || !BOOKING_NONCE) { window.alert(S.bookingNotConfigured || S.bookingSaveError || "Booking unavailable."); return; } var card = b.closest(".travelgo-ss-flight-card"); var rawIdx = card ? card.getAttribute("data-travelgo-flight-index") : null; var idx = rawIdx != null ? parseInt(rawIdx, 10) : NaN; var flight = !isNaN(idx) && state.lastRenderedFlights ? state.lastRenderedFlights[idx] : null; if (!flight) { window.alert(S.bookingSaveError || "Could not read this flight."); return; } var fd = new FormData(); fd.append("action", "travelgo_save_flight_booking"); fd.append("nonce", BOOKING_NONCE); fd.append("payload", JSON.stringify(buildBookingPayload(flight))); b.disabled = true; fetch(AJAX_URL, { method: "POST", body: fd, credentials: "same-origin" }) .then(function (r) { return r.json(); }) .then(function (res) { b.disabled = false; if (!res || !res.success || !res.data || !res.data.redirect) { var msg = (res && res.data && res.data.message) || S.bookingSaveError || "Error"; window.alert(msg); return; } window.location.href = res.data.redirect; }) .catch(function () { b.disabled = false; window.alert(S.bookingSaveError || "Error"); }); }); }); } function refreshNearbyHotelsStrip() { if (!isResults || !resultsRoot) return; var root = resultsRoot.querySelector("#travelgo-fr-nearby-root"); var hint = resultsRoot.querySelector("#travelgo-fr-nearby-hint"); var grid = resultsRoot.querySelector("#travelgo-fr-nearby-grid"); var chk = resultsRoot.querySelector("#travelgo-fr-stay-airport"); if (!root || !grid) return; if (!HAS_GOOGLE_MAPS || !MAPS_NEARBY_URL) { root.hidden = true; return; } if (!state.to || state.to.length !== 3) { root.hidden = true; return; } root.hidden = false; if (hint) { hint.textContent = S.loadingHotelsNearAirport || "Loading hotels near airport…"; } grid.innerHTML = ""; if (chk && !chk.dataset.travelgoWired) { chk.dataset.travelgoWired = "1"; chk.addEventListener("change", function () { refreshNearbyHotelsStrip(); }); } fetch(MAPS_NEARBY_URL, { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json" }, credentials: "same-origin", body: JSON.stringify({ airport_iata: state.to, stay_near_airport: !!(chk && chk.checked), }), }) .then(function (r) { return r.json().then(function (data) { return { ok: r.ok, data: data }; }); }) .then(function (wrapped) { var data = wrapped && wrapped.data; if (!wrapped || !wrapped.ok || !data || !Array.isArray(data.places)) { if (hint) { hint.textContent = (data && data.message) || S.hotelsNearAirportError || "Could not load nearby hotels."; } return; } if (hint) hint.textContent = ""; if (!data.places.length) { if (hint) hint.textContent = S.noHotelsNearAirport || "No lodging results."; return; } grid.innerHTML = data.places .map(function (p) { var dollars = typeof p.price_level === "number" && p.price_level >= 0 ? " · " + "$$$$$".slice(0, Math.min(4, p.price_level + 1)) : ""; var rate = p.rating && isFinite(Number(p.rating)) ? "\u2B50 " + Number(p.rating).toFixed(1) : ""; var dist = p.distance_km != null && isFinite(Number(p.distance_km)) ? " · " + p.distance_km + " km" : ""; var href = String(p.maps_url || "#").replace(/"/g, """); return ( '

' + escapeHtml(p.name || "") + '

' + escapeHtml(rate + dollars + dist) + '

' + escapeHtml(S.openInMaps || "Open in Google Maps") + "
" ); }) .join(""); }) .catch(function () { if (hint) { hint.textContent = S.hotelsNearAirportError || "Could not load nearby hotels."; } }); } function handleResults(data) { var ts = data && data._travelgo_search; if (ts && typeof ts === "object") { if (ts.outbound_date) { var oIso = normalizeSearchDateToIso(String(ts.outbound_date)); if (oIso) state.departDate = oIso; } if (ts.return_date) { var rIso = normalizeSearchDateToIso(String(ts.return_date)); if (rIso) state.returnDate = rIso; } } var best = data.best_flights || []; var other = data.other_flights || []; var flights = best.concat(other); flights.forEach(function (f) { var hasServerPricing = f && typeof f === "object" && f._travelgo_pricing && typeof f._travelgo_pricing === "object" && isFinite(Number(f._travelgo_base_price || 0)); if (hasServerPricing) { var pp = f._travelgo_pricing; f._travelgo_base_price = Number(f._travelgo_base_price || 0); f._travelgoPricing = { baseFare: Number(pp.base_fare || f._travelgo_base_price || 0), markupPercent: Number(pp.markup_percent || 0), markupAmount: Number(pp.markup_amount || 0), convenienceFee: Number(pp.convenience_fee || 0), grossTotal: Number(pp.gross_total || f.price || 0), couponType: String(pp.coupon_type || "none"), couponValue: Number(pp.coupon_value || 0), couponCode: String(pp.coupon_code || ""), couponAmount: Number(pp.coupon_amount || 0), cancelType: String(pp.cancel_type || "none"), cancelValue: Number(pp.cancel_value || 0), cancelAmount: Number(pp.cancel_amount || 0), estimatedRefund: Number(pp.estimated_refund || 0), refundNote: String(pp.refund_note || ""), }; f.price = Math.round(Number(f.price || f._travelgoPricing.grossTotal || 0)); } else { var original = Number(f.price || 0); if (!isFinite(original)) original = 0; f._travelgo_base_price = original; var p = applyPricingToFare(original); f._travelgoPricing = p; f.price = Math.round(Number(p.grossTotal || 0)); } }); flights = flights.filter(function (f) { var display = Number(f.price || 0); var base = Number(f._travelgo_base_price || 0); return isFinite(display) && display > 1 && isFinite(base) && base > 0; }); state.allFlights = flights; state.currentResults = flights; state.currentMeta = data; var piBlock = res$("price-insight"); if (piBlock && data.price_insights) { var pi = data.price_insights; var allAdjustedPrices = flights.map(function (f) { return Number(f.price || 0); }).filter(function (p) { return isFinite(p) && p > 0; }).sort(function (a, b) { return a - b; }); var low = allAdjustedPrices.length ? allAdjustedPrices[0] : (pi.lowest_price || 0); var rangeLow = allAdjustedPrices.length ? allAdjustedPrices[Math.floor((allAdjustedPrices.length - 1) * 0.25)] : ((pi.typical_price_range && pi.typical_price_range.low_price) || 0); var rangeHigh = allAdjustedPrices.length ? allAdjustedPrices[Math.floor((allAdjustedPrices.length - 1) * 0.75)] : ((pi.typical_price_range && pi.typical_price_range.high_price) || 0); var levelMap = { low: { label: S.priceLow || "Low price", cls: "travelgo-ss-pi-low" }, typical: { label: S.priceTypical || "Typical price", cls: "" }, high: { label: S.priceHigh || "High price", cls: "travelgo-ss-pi-high" }, }; var lv = levelMap[pi.price_level] || levelMap.typical; piBlock.innerHTML = '
' + '
💡
' + '
' + (S.lowestFound || "Lowest found") + ": " + formatMoney(low) + "" + (S.typicalRange || "Typical range") + " " + formatMoney(rangeLow) + " – " + formatMoney(rangeHigh) + "
" + '
' + lv.label + "
"; } else if (piBlock) piBlock.innerHTML = ""; var list = res$("flight-list"); var countEl = res$("results-count"); if (!flights.length) { state.allFlights = []; if (list) list.innerHTML = '
' + '
✈️

' + (S.noFlightsTitle || "No flights found") + "

" + (S.noFlightsHint || "Try adjusting your dates or airports.") + "

"; if (countEl) countEl.textContent = S.zeroFlights || "0 flights found"; if (isResults && resultsRoot) { var nr = resultsRoot.querySelector("#travelgo-fr-nearby-root"); if (nr) nr.hidden = true; } return; } if (isResults) { rebuildAirlineFilters(flights); syncPriceSliderFromData(); updateFilterHints(); applyFiltersAndRender(); refreshNearbyHotelsStrip(); } } function buildTfQuery() { var p = new URLSearchParams(); p.set("tf_from", state.from); p.set("tf_to", state.to); p.set("tf_dep", state.departDate); p.set("tf_trip", state.tripType); if (state.tripType === "round_trip" && state.returnDate) p.set("tf_ret", state.returnDate); p.set("tf_adults", state.adults); p.set("tf_ch", state.children); p.set("tf_inf", state.infants); p.set("tf_class", state.travelClass); p.set("tf_stops", state.stops); p.set("tf_sort", state.sortBy); p.set("tf_fare", state.fareType); p.set("tf_cur", state.currency); return p.toString(); } function normCode(s, fallback) { var c = String(s || "") .toUpperCase() .replace(/[^A-Z]/g, "") .substring(0, 3); return c.length === 3 ? c : fallback; } function parseTfNum(p, key, defVal, min, max) { var v = p.get(key); if (v == null || v === "") return String(defVal); var n = parseInt(v, 10); if (isNaN(n)) return String(defVal); n = Math.max(min, Math.min(max, n)); return String(n); } function syncSortUi() { var sp = resultsRoot ? resultsRoot.querySelector("#travelgo-ss-sort-pills") : null; if (!sp) return; sp.querySelectorAll(".travelgo-ss-sort-pill").forEach(function (pill) { var d = pill.getAttribute("data-sort") || ""; pill.classList.toggle("is-active", d === state.sortBy); }); } function initResultsFromUrl() { var p = new URLSearchParams(window.location.search); state.from = normCode(p.get("tf_from"), "DEL"); state.to = normCode(p.get("tf_to"), "BOM"); var trip = p.get("tf_trip") || "one_way"; if (trip !== "one_way" && trip !== "round_trip") { trip = "one_way"; } state.tripType = trip; state.departDate = normalizeSearchDateToIso(p.get("tf_dep") || "") || String(p.get("tf_dep") || "").trim(); state.returnDate = normalizeSearchDateToIso(p.get("tf_ret") || "") || String(p.get("tf_ret") || "").trim(); state.adults = parseTfNum(p, "tf_adults", 1, 1, 9); state.children = parseTfNum(p, "tf_ch", 0, 0, 6); state.infants = parseTfNum(p, "tf_inf", 0, 0, 6); var tc = p.get("tf_class") || "economy"; var okc = { economy: 1, premium_economy: 1, business: 1, first_class: 1, }; state.travelClass = okc[tc] ? tc : "economy"; var st = p.get("tf_stops") || "any"; var oks = { any: 1, nonstop: 1, one_stop_or_fewer: 1 }; state.stops = oks[st] ? st : "any"; var rawSort = p.get("tf_sort") || "top_flights"; var sortOk = { top_flights: 1, price: 1, duration: 1, departure_time: 1, popularity: 1, }; state.sortBy = sortOk[rawSort] ? rawSort : "top_flights"; state.fareType = p.get("tf_fare") || "regular"; state.currency = normalizeCurrency(p.get("tf_cur") || (cfg.defaultCurrency || "INR")); var rsCur = document.getElementById("travelgo-fr-currency-select"); if (rsCur) rsCur.value = state.currency; var headline = $("travelgo-fr-headline"); if (headline) { headline.textContent = (S.flightsFromPrefix || "Flights from ") + cityLine(state.from) + (S.flightsFromToMid || " to ") + cityLine(state.to) + (state.tripType === "round_trip" ? S.flightsAndBack || ", and back" : ""); } var routeTitle = $("travelgo-ss-route-title"); if (routeTitle) { routeTitle.textContent = state.from + " → " + state.to; } syncSortUi(); ensureResultsLoadingOverlay(); renderFareCalendar(); fetchFlightData(); } function ensureResultsLoadingOverlay() { if (!resultsRoot || res$("loading-overlay")) return; var host = document.createElement("div"); host.id = "travelgo-ss-loading-overlay"; host.className = "travelgo-fr-loading-overlay"; host.hidden = true; host.innerHTML = '
' + '' + '' + escapeHtml(S.fetching || "Fetching results…") + "
"; resultsRoot.appendChild(host); } async function fetchFlightData() { if (state.tripType === "multi_city") return; if (!state.from || !state.to || !state.departDate) return; if (state.tripType === "round_trip" && !state.returnDate) return; var list = res$("flight-list"); var loading = res$("loading-overlay"); if (loading) loading.hidden = false; var countEl = res$("results-count"); if (list) list.innerHTML = renderSkeletons(); var piBlock = res$("price-insight"); if (piBlock) piBlock.innerHTML = ""; if (countEl) countEl.textContent = S.fetching || "Fetching results…"; var apiTrip = state.tripType === "one_way" ? "one_way" : "round_trip"; var params = new URLSearchParams({ engine: "google_flights", api_key: API_KEY, departure_id: state.from, arrival_id: state.to, outbound_date: state.departDate, flight_type: apiTrip, travel_class: state.travelClass, adults: state.adults, stops: state.stops, sort_by: apiSortBy(), currency: state.currency, gl: "in", hl: "en", }); if (apiTrip === "round_trip" && state.returnDate) { params.set("return_date", state.returnDate); } try { var data; if (useRestProxy) { var payload = { departure_id: state.from, arrival_id: state.to, outbound_date: state.departDate, flight_type: apiTrip, travel_class: state.travelClass, adults: parseInt(state.adults, 10) || 1, children: parseInt(state.children, 10) || 0, stops: "any", sort_by: apiSortBy(), currency: state.currency, gl: "in", hl: "en", }; if (apiTrip === "round_trip" && state.returnDate) { payload.return_date = state.returnDate; } var res = await fetch(REST_URL, { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json" }, credentials: "same-origin", body: JSON.stringify(payload), }); var rawText = await res.text(); if (!res.ok) { var errObj = {}; try { errObj = JSON.parse(rawText); } catch (e) {} var msg = (errObj && errObj.message) || (errObj && errObj.data && errObj.data.message) || "API error " + res.status; throw new Error(String(msg)); } try { data = JSON.parse(rawText); } catch (e2) { throw new Error("Invalid JSON from server"); } } else { var resLegacy = await fetch(BASE_URL + "?" + params.toString()); if (!resLegacy.ok) throw new Error("API error " + resLegacy.status); data = await resLegacy.json(); } handleResults(data); } catch (err) { if (list) { list.innerHTML = '
' + "" + (S.couldNotLoad || "Could not load flights") + "
" + escapeHtml(err.message || String(err)) + "
"; } if (countEl) countEl.textContent = S.errorCount || "Error loading results"; } finally { if (loading) loading.hidden = true; } } function resolveFieldToIata(el) { if (!el) return null; var selected = el.getAttribute("data-selected-code"); if (selected && /^[A-Z]{3}$/.test(selected)) return selected; var raw = String(el.value || "").trim(); if (!raw) return null; return null; } function doSearch() { if (state.tripType === "multi_city") return; if (!isHome) return; var fe = $("travelgo-ss-from-input"); var te = $("travelgo-ss-to-input"); var fc = resolveFieldToIata(fe); var tc = resolveFieldToIata(te); if (fc) updateAirport("from", fc); if (tc) updateAirport("to", tc); if (!state.from || !state.to || !state.departDate) return; if (state.tripType === "round_trip" && !state.returnDate) return; if (!fc || !tc) { window.alert( S.airportInvalid || "Please select origin and destination from the airport dropdown." ); return; } if (!RESULTS_PAGE_URL) { window.alert( S.configureResultsPage || "Choose a flight results page under Appearance → Customize → Flights." ); return; } var sep = RESULTS_PAGE_URL.indexOf("?") >= 0 ? "&" : "?"; window.location.href = RESULTS_PAGE_URL + sep + buildTfQuery(); } if (isHome && homeRoot) { var searchBtn = $("travelgo-ss-search"); if (searchBtn) searchBtn.addEventListener("click", doSearch); initDates(); updateAirport("from", "DEL"); updateAirport("to", "BOM"); updatePaxSummary(); setTripType("one_way"); } if (isResults) { var resCur = document.getElementById("travelgo-fr-currency-select"); if (resCur) { resCur.value = state.currency; resCur.addEventListener("change", function () { state.currency = normalizeCurrency(resCur.value); syncResultsUrl(); fetchFlightData(); renderFareCalendar(); }); } wireResultsFilters(); initResultsFromUrl(); } })();